/*
// @title: 字符串相关函数
// @auth: 技术狼(jishulang.com)
*/
package fun

import (
	"database/sql"
	"fmt"
	"math/big"
	"math/rand"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const NEWLINE = "\n"

// @title: 生成N个随机字符
// @param: 个数(int)
// @return: 字符串(string)
// @description: a-z,A-Z,0-9
// @date: 2022/7/3 21:07
func RandString(n int) string {
	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	var src = rand.NewSource(time.Now().UnixNano())
	const (
		letterIdxBits = 6
		letterIdxMask = 1<<letterIdxBits - 1
		letterIdxMax  = 63 / letterIdxBits
	)
	b := make([]byte, n)
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}
	return string(b)
}

// @title: string转int
// @param: 字符串(str)
// @return: int, error
// @description:
// @date: 2022/7/3 21:18
func StrToInt(val string) (int, error) {
	i, err := strconv.Atoi(val)
	if err != nil {
		return 0, err
	}
	return i, nil
}

// @title: string转int64
// @param: 字符串(str)
// @return: int64，error
// @description:
// @date: 2022/7/3 21:25
func StrToInt64(val string) (int64, error) {
	i, err := strconv.ParseInt(val, 10, 64)
	if err != nil {
		{
			f64, err := strconv.ParseFloat(val, 64)
			if err == nil {
				return int64(f64), nil
			}
		}

		if IsScientificNotation(val) {
			v, err := RetainDecimalPlaces(val, 0)
			if err != nil {
				return 0, err
			}
			return StrToInt64(v)
		}
		return 0, err
	}
	return i, nil
}

// @title: string转uint64
// @param: 字符串(str)
// @return: uint64，error
// @description:
// @date: 2022/7/3 21:28
func StrToUint64(val string) (uint64, error) {
	i, err := StrToInt64(val)
	if err != nil {
		return 0, err
	}

	ui := uint64(i)

	return ui, nil
}

// @title: string转Float64
// @param: 字符串(str)
// @return: float64, error
// @description:
// @date: 2022/7/3 21:29
func StrToFloat64(val string) (float64, error) {
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return 0, err
	}
	return f, nil
}

// StringToFloat32 将字符串安全转换为 float32
func StrToFloat32(s string) (float32, error) {
	f64, err := strconv.ParseFloat(s, 32) // 按 float32 精度解析
	if err != nil {
		return 0, err
	}
	return float32(f64), nil
}

// @title: string转*big.Float
// @param: 字符串(str)
// @return: *big.Float, bool
// @description:
// @date: 2022/7/3 21:31
func StrToBigFloat(str string) (*big.Float, bool) {
	bigval, ok := new(big.Float).SetString(str)
	if !ok {
		return nil, false
	}
	return bigval, true
}

// @title: string转*big.Int
// @param: (字符)string，(精度位)int64
// @return: *big.Int, bool
// @description:
// @auth: 技术狼
// @date: 2022/7/3 21:00
func StrToBigInt(str string, exp int64) (*big.Int, bool) {
	expStr := fmt.Sprintf("1e%d", exp) // "1e" + "8"

	amount := new(big.Rat)
	_, ok := amount.SetString(str)
	if !ok {
		return nil, false
	}
	bigexp := new(big.Rat)
	_, ok2 := bigexp.SetString(expStr)
	if !ok2 {
		return nil, false
	}

	result := new(big.Rat).Mul(amount, bigexp)

	result.FloatString(0)

	return new(big.Int).SetString(result.FloatString(0), 10)
}

// @title: 某字符串是否存在数组中
// @param: 字符(string)，数组(array)
// @return: 布尔(bool)
// @description:
// @date: 2022/7/3 21:33
func StrIsInArr(target string, str_array []string) bool {
	sort.Strings(str_array)
	index := sort.SearchStrings(str_array, target)
	//index的取值：[0,len(str_array)]
	if index < len(str_array) && str_array[index] == target { //需要注意此处的判断，先判断 &&左侧的条件，如果不满足则结束此处判断，不会再进行右侧的判断
		return true
	}
	return false
}

// @title: string是否是浮点
// @param: 字符串(str)
// @return: bool
// @description:
// @date: 2024/7/16 19:15
func StrIsFloat(val string) bool {
	_, err := strconv.ParseFloat(val, 64)
	return err == nil
}

// @title: 某字符是否存在字符串中(字符串是否包含某字符)
// @param: 某字符(string)，字符串(string)
// @return: bool
// @description:
// @date: 2024/7/16 19:15
func IsInString(target string, strs string) bool {
	if find := strings.Contains(strs, target); find {
		return true
	}
	return false
}

// @title: 是否是字符类型
// @param:
// @return: bool
// @description:
// @date: 2024/7/16 19:15
func IsString(val interface{}) bool {
	if GetType(val) == "string" {
		return true
	}
	return false
}

// @title: 字符串打散为数组
// @param: 分割字符串(string)，要分割的字符串(string)
// @return: 数组(map[int]string)
// @description:
// @date: 2024/7/16 19:15
func Explode(separator, str string) []string {
	return strings.Split(str, separator)
}

// @title: 获取字符串中字符的数量
// @param: 字符串(string)
// @return: 数量(int)
// @description:
// @date: 2024/7/16 19:15
func MbStrLen(str string) int {
	return len([]rune(str))
}

// @title: 字符串首字母转成小写
// @param: 字符串(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func Lcfirst(str string) string {
	if len(str) == 0 {
		return ""
	}

	s := []rune(str)
	if s[0] >= 65 && s[0] <= 90 {
		s[0] += 32
	}
	return string(s)
}

// @title: 字符串首字母转成大写
// @param: 字符串(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func Ucfirst(str string) string {
	if len(str) == 0 {
		return ""
	}

	s := []rune(str)
	if s[0] >= 97 && s[0] <= 122 {
		s[0] -= 32
	}
	return string(s)
}

// @title: 字符串全转大写
// @param: 字符串(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func StrToUpper(str string) string {
	return strings.ToUpper(str)
}

// @title: 字符串全转小写
// @param: 字符串(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func StrToLower(str string) string {
	return strings.ToLower(str)
}

// @title: 每个单词的首字符转换为大写
// @param: 字符串(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func Ucword(str string) string {
	return strings.Title(str)
}

// @title: 移除字符串两侧的字符
// @param: 字符串(string)，要移除的字符(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func Trim(str, charlist string) string {
	if charlist == "" {
		charlist = " \r\n\t\x0B"
	}
	return strings.Trim(str, charlist)
}

// @title: 去除全部空格
// @param: 字符串(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func TrimAll(str string) string {
	if len(str) == 0 {
		return ""
	}
	return strings.Join(strings.Fields(str), "")
}

// @title: 移除字符串左侧的字符
// @param: 字符串(string)，要移除的字符(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func Ltrim(str, charlist string) string {
	if charlist == "" {
		charlist = " \r\n\t\x0B"
	}
	return strings.TrimLeft(str, charlist)
}

// @title: 移除字符串右侧的字符
// @param: 字符串(string)，要移除的字符(string)
// @return: 字符串(string)
// @description:
// @date: 2024/7/16 19:15
func Rtrim(str, substr string) string {
	if substr == "" {
		substr = " \r\n\t\x0B"
	}
	/*
		TrimRight 是设计来移除单个字符的，而不是整个子串,
		如果提供了一个子串作为 charlist，它实际上会尝试从右侧开始移除该子串中的每一个字符，
		直到遇到不匹配的字符为止。
	*/
	return strings.TrimRight(str, substr)
}

// @title: 返回字符串中第一个字符的 ASCII 值
// @param: 字符串(string)
// @return: ASCII值(int)
// @description:
// @date: 2024/7/16 19:15
func Ord(str string) int {
	if str == "" {
		return 0
	}
	s := []rune(str)
	return int(s[0])
}

// @title: 传ASCII值返回字符
// @param: ASCII值(int32)
// @return: 字符(string)
// @description:
// @date: 2024/7/16 19:15
func Chr(ascii int32) string {
	return string(ascii)
}

// @title: 指定字符串重复N次
// @param: 字符串(string)，次数(int)
// @return: 字符(string)
// @description:
// @date: 2024/7/16 19:15
func StrRepeat(input string, multiplier int) string {
	return strings.Repeat(input, multiplier)
}

// @title: 字符替换
// @param: 要替换的字符串(string)，旧字符(string)，新字符(string)
// @return: string
// @description:
// @date: 2022/7/4 22:14
func StrReplace(strs string, oldStr string, newStr string) string {
	return strings.Replace(strs, oldStr, newStr, -1)
}

// @title: 是否是科学计数
// @param: str（科学计数。如:"4.843671275939215e+21"）
// @return: bool
// @description:
// @date: 2024/6/11 22:30
func IsScientificNotation(s string) bool {
	// 正则表达式匹配科学计数法
	// 匹配形如 [+-]?\d+(\.\d+)?[eE][+-]?\d+ 的字符串
	regex := regexp.MustCompile(`^[+-]?\d+(\.\d+)?[eE][+-]?\d+$`)
	return regex.MatchString(s)
}

// @title: 将科学计数转换为字符串数字
// @param: val（科学计数。如:4.843671275939215e+21）
// @param: decimalPlaces（精度，小数点位数）
// @return: string, error
// @description:
// @date: 2024/6/1 10:21
func ScientificNotationToStrings(val string, decimalPlaces uint) (string, error) {
	// 解析科学记数法字符串为float64
	value, err := strconv.ParseFloat(val, 64) // 使用的是IEEE 754双精度浮点数标准，它只能保证大约15-17位的十进制精度。
	if err != nil {
		return "", err
	}

	str := fmt.Sprintf("%v", val)
	arr := Explode(".", str)
	if len(arr[0]) >= 15 || len(arr[1]) >= 13 {
		// 创建一个新的big.Float实例，用于保存转换后的数值
		value := new(big.Rat)
		// 尝试将科学记数法字符串解析为big.Float
		_, ok := value.SetString(val)
		if !ok {
			return "", fmt.Errorf("Failed to parse scientific notation: %s", val)
		}

		decimalStr := value.FloatString(int(decimalPlaces))
		return decimalStr, nil
	}

	// 使用%.decimalPlacesf格式化，根据参数指定小数位数
	decimalStr := fmt.Sprintf("%.*f", int(decimalPlaces), value)

	return decimalStr, nil
}

// @title: 获取小数点后的位数
// @param: str
// @return: int
// @description:（非浮点值、科学计数返回0）
// @date: 2024/6/11 22:32
func CountDecimalPlaces(val string) int {
	if !StrIsFloat(val) {
		return 0
	}
	if IsScientificNotation(val) {
		return 0
	}
	// 将字符串以小数点为分隔符进行切分
	parts := strings.Split(val, ".")
	// 如果小数点后有部分，则表示有位数
	if len(parts) > 1 && len(parts[1]) > 0 {
		// 返回小数点后的位数
		return len(parts[1])
	}
	// 如果没有小数点或者小数点后没有数字，则返回0
	return 0
}

// @title: 保留n位小数
// @param: amount 金额
// @param: precision 要保留多少位小数
// @return: int
// @description:
// @date: 2024/6/11 22:34
func RetainDecimalPlaces(amount string, precision uint) (string, error) {
	floatInput, err := strconv.ParseFloat(amount, 64)
	if err != nil {
		return amount, err
	}
	result := fmt.Sprintf("%.*f", precision, floatInput)
	return result, nil
}

// @title: 金额精度
// @param: amount（金额。如:4.8436712000000）
// @param: maxDecimals（小数位数）
// @return: string
// @description:
// @date: 2024/6/11 22:35
func AmountDecimal(amount string, maxDecimals uint) string {
	if IsScientificNotation(amount) {
		amount, _ = ScientificNotationToStrings(amount, maxDecimals)
	} else {
		decimalLen := CountDecimalPlaces(amount)
		if uint(decimalLen) > maxDecimals {
			a, _ := RetainDecimalPlaces(amount, uint(maxDecimals))
			amount = a
		}
	}

	amount = RemoveAllZerosFromLastDecimal(amount)

	return amount
}

// @title: 移出小数点后无效的数
// @param: amount（金额。如:4.8436712000000）
// @return: string
// @description:
// @date: 2024/6/1 10:21
func RemoveAllZerosFromLastDecimal(amount string) string {
	if amount == "" || amount == "0" {
		return amount
	}
	if idx := strings.LastIndex(amount, "."); idx != -1 {
		rightStr := strings.TrimRight(amount[idx+1:], "0")
		amount = amount + "." // 确保小数点还在，即使所有小数位都是0
		leftStr := amount[:idx]
		if rightStr == "" {
			return leftStr
		}
		return leftStr + "." + rightStr
	}
	return amount
}

// @title: 字符串是否是纯数字
// @param: str
// @return: bool
// @description:
// @date: 2024/6/1 10:21
func StrIsNumeric(str string) bool {
	matched, _ := regexp.MatchString(`^\d+$`, str)
	return matched
}

// @title: 获取浮点值的整数位和小数位
// @param: str
// @return: bool
// @description:
// @date: 2024/6/1 10:21
func FloatStrPlaces(floatStr string) (string, int, string, int) {
	if floatStr == "" || floatStr == "0" {
		return "0", 1, "0", 0
	}
	floatStr = RemoveAllZerosFromLastDecimal(floatStr)
	parts := strings.Split(floatStr, ".")
	intPart := parts[0] // 整数部分
	fracPart := ""      // 小数部分默认为空字符串
	if len(parts) > 1 {
		fracPart = parts[1]
	}

	return intPart, len(intPart), fracPart, len(fracPart)
}

// @title: 生成N位纯数字的字符串
// @param: int
// @return: string
// @description:
// @date: 2024/6/1 10:21
func GenerateRandomNumberString(length int) string {
	// 设置随机数种子
	rand.Seed(time.Now().UnixNano())

	// 定义数字字符集
	const digits = "0123456789"

	// 创建一个字节切片存储结果
	result := make([]byte, length)

	// 随机选择数字字符
	for i := 0; i < length; i++ {
		result[i] = digits[rand.Intn(len(digits))]
	}

	return string(result)
}

// @title: 字符是否存在字符串中
// @param: 字符，字符串
// @return: bool
// @description:
// @date: 2024/6/1 10:21
func StrIsInString(target string, str string) bool {
	if find := strings.Contains(str, target); find {
		return true
	}
	return false
}

// @title: 转换为sql.NullString
// @param: 字符串
// @return: sql.NullString
// @description:
// @date: 2024/6/1 10:21
func ToNullString(s string) sql.NullString {
	return sql.NullString{
		String: s,
		Valid:  s != "",
	}
}

// @title: 获取字符串首字符
// @param: 字符串
// @return: 字符串
// @description:
// @date: 2024/6/1 10:21
func FirstCharacter(s string) string {
	if len(s) > 0 {
		return string(s[0])
	}
	return s
}
